﻿using System.Threading;
using System.Diagnostics;
using System;
using System.Text;
using System.IO;
using PPJoy_dotNET;

namespace DroidPad
{
    /// Main thread for DroidPad, which recieves data and sends data to various outputs
    public class DPThread
    {
        //private Thread th;
        private connHandler connhandler;
        //private bool running;
        //private bool sending;
        //private object sendingLock = new object();
        private bool stop;
        private bool s;
        private object stopLock = new object();
        private bool joystickAllowed;
        private bool debug;
        private DPMode dpMode;
        private int ppNum, ppUpd;
        private PPJoyProvider ppProv=null;
        private InfoDecoder infoDecoder;

        private InfoDecoder.DPItem[] dpItems;
        //bool[] pressedButtons = new bool[] { false, false, false };
        private bool[] tmpButtons = new bool[1];
        private int tmpButtonsCount = 0;
        private int[] tmpAxis = new int[1];
        private int tmpAxisCount = 0;
        //InfoDecoder.DPItem[] oldDpItems = new InfoDecoder.DPItem[0];

        /// <param name="joystickAllowed">Whether joystick is allowed</param>
        /// <param name="debug">Debug mode?</param>
        /// <param name="ppNum">PPJoy controller number</param>
        /// <param name="ppUpd">PJoy update interval</param>

        public enum DPMode
        {
            Gun,
            Wheel,
            Playstation,

        }

        public delegate void StopHandler();
        /// Called when the thread is stopping
        public event StopHandler Stopped;

        private void SendStop()
        {
            connhandler.close();   
            //th.Abort();
            if(ppProv != null) ppProv.Stop();
        }

        public delegate void InitStatHandler(DPMode mode);
        /// Returns the mode to the main window.
        public event InitStatHandler InitStatSend;
        private void SendInitStat(DPMode mode)
        {
            if (InitStatSend != null) InitStatSend(mode);
        }

        /// Main execution thread of DroidPAd
        /// <param name="joystickAllowed">Whether joystick is allowed</param>
        /// <param name="debug">Debug mode?</param>
        /// <param name="ppNum">PPJoy controller number</param>
        /// <param name="ppUpd">PJoy update interval</param>
        public DPThread( bool joystickAllowed, bool debug, int ppNum, int ppUpd)
        {
            this.joystickAllowed = joystickAllowed;
            this.debug = debug;
            this.ppNum = ppNum;
            this.ppUpd = ppUpd;
        }

        /// Starts the main thread
        public void Start()
        {
            Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
            // SendStatMessage("Started, Listening to Incoming Connections on port :" + Convert.ToString(Properties.Settings.Default.Port));
            infoDecoder = new InfoDecoder();
            connhandler = new connHandler(
                                            new modeInitializeDelegate(modeInitialize),
                                            new dataAnalyzeDelegate(dataAnalyze),
                                            new stopJoystickDelegate(stopJoystick));
            connhandler.start();
        }

        /// Stops the main thread
        public void Stop()
        {
            //bool s;
            SendStop();

  /*          lock (sendingLock)
            {
                s = sending;
            }
            if (s)
            {
                lock (stopLock)
                {
                    stop = true;
                }
                
                try
                {
                    th.Join(3500);
                }
                catch { }
                try
                {
                    th.Abort();
                }
                catch { }
                 
            }
            lock (stopLock)
            {
                stop = false;
            }
            lock (sendingLock)
            {
                sending = false;
            }
            */
            if (Stopped != null) Stopped();
        }

        private delegate void stopJoystickDelegate();
        private void stopJoystick()
        {
            ppProv.Stop();
        }

        private delegate bool modeInitializeDelegate(String str);
        private bool modeInitialize(String str)
        {
            // Getting mode information
            /*while (true)
            {
                //str = readString(ns);
                //str = connhandler.readData();
                if (str != "")
                    break;
                if (debug) SendStatMessage("Empty packet sent... (why?)");
            }*/
            string mode = null;
            try
            {
                int ms = str.IndexOf("<MODE>") + 6;
                int mf = str.IndexOf("</MODE>");
                mode = str.Substring(ms, mf - ms);
            }
            catch
            {
                if (debug) DPsf.SendStatMessage("ERROR: Mode not sent correctly, ignoring...");
                return false;
                //mode = "1";
            }
            dpMode = DPMode.Wheel;
            if (mode.StartsWith("gun", true, null)) dpMode = DPMode.Gun;
            else if (mode.StartsWith("playstation", true, null)) dpMode = DPMode.Playstation;

            SendInitStat(dpMode);
            if (joystickAllowed)
            {
                switch (dpMode)
                {
                    case DPMode.Wheel:
                        break;
                    case DPMode.Gun:
                        break;
                    case DPMode.Playstation:
                        break;
                }
                ppProv = new PPJoyProvider(debug);
                ppProv.Start(ppNum, ppUpd);
                return true;
            }
            else
            {
                byte[] b = new ASCIIEncoding().GetBytes("<STOP>");
               // ns.Write(b, 0, b.Length);
                connhandler.writeData(b, b.Length);
               // MessageBox.Show("To use Joystick mode, please install & configure PPJoy. (See manual)", "Install PPJoy");
               // SendStop();
                return false;
            }
          /*  lock (sendingLock)
            {
                sending = true;
            }*/
        }

        private delegate void dataAnalyzeDelegate(String str2);
          private void dataAnalyze(String str2){
                if (str2 == null)
                {
                    SendStop();
                    return;
                }
                dpItems = infoDecoder.Decode(str2);
                //Values processed correctly

                joystickLogic();
                //Values sent to PPJoy handler

                lock (stopLock)
                {
                    s = stop;
                }
                if (s)
                {
                    if (debug) DPsf.SendStatMessage("Stopping...");
                    byte[] b = new ASCIIEncoding().GetBytes("<STOP>");
                    connhandler.writeData(b, b.Length);
                    SendStop();
                    return;
                }
            }
        
        private void joystickLogic()
        {
            // Count number of 'Axises' & buttons needed
            tmpAxisCount = 0;
            tmpButtonsCount = 0;
            for (int i = 0; i < dpItems.Length; i++)
            {
                if (dpItems[i].type == InfoDecoder.DPItem.Type.Axis)
                    tmpAxisCount += 2;
                else if (dpItems[i].type == InfoDecoder.DPItem.Type.Slider)
                    tmpAxisCount++;
                else // Button
                    tmpButtonsCount++;
            }
            tmpAxis = new int[tmpAxisCount];
            tmpButtons = new bool[tmpButtonsCount];
            
            //Arrange values into data
            tmpAxisCount = 0;
            tmpButtonsCount = 0;
            for (int i = 0; i < dpItems.Length; i++)
            {
                if (dpItems[i].type == InfoDecoder.DPItem.Type.Axis)
                {
                    tmpAxis[tmpAxisCount] = dpItems[i].axisX;
                    tmpAxisCount++;
                    tmpAxis[tmpAxisCount] = dpItems[i].axisY;
                    tmpAxisCount++;
                }
                else if (dpItems[i].type == InfoDecoder.DPItem.Type.Slider)
                {
                    tmpAxis[tmpAxisCount] = dpItems[i].axisX;
                    tmpAxisCount++;
                }
                else // Button
                {
                    tmpButtons[tmpButtonsCount] = dpItems[i].pressed;
                    tmpButtonsCount++;
                }
            }

            ppProv.Update(tmpAxis, tmpButtons);
        }

        public static class StatFunc
        {
            public static double ClipBorders(double a, int top, int bottom)
            {
                if (a < bottom)
                {
                    a = bottom;
                }
                if (a > top)
                {
                    a = top;
                }
                return a;
            }
        }
    }
}